package com.canoe.parameter;

import com.canoe.common.CanoeException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 执行参数解析器
 *
 * @author: btm
 * @time: 20:40 2020/9/22
 **/
public final class ParameterParser<T> {

    private static final String ARGUMENT_START_WITH = "--";
    /**
     * 解析参数数组，返回参数对象
     *
     * @param clazz
     * @param args
     * @return
     */
    public T parser(Class<? extends T> clazz, IParameter parameter, String... args) {
        if (null == args || args.length <= 0) {
            return null;
        }
        T t = null;
        String currentParserMethodName = "";
        try {
            t = clazz.newInstance();
            for (int i = 0; i < args.length;) {
                String setMethodName = parameter.parserParameterFileSetMethodName(args[i]);
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(setMethodName)) {
                        currentParserMethodName = method.getName();
                        //获得当前域的set方法共有几个参数及对应的类型
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        if (parameterTypes.length != 1) {
                            throw new CanoeException("Number of method argument must be one");
                        }
                        Class argumentType = parameterTypes[0];
                        List<String> paramList = new ArrayList<>(10);
                        for (i++; i < args.length && !args[i].startsWith(ARGUMENT_START_WITH); i++) {
                            paramList.add(args[i]);
                        }
                        if (argumentType.getName().startsWith("[L")) {
                            String[] argumentArr = new String[paramList.size()];
                            paramList.toArray(argumentArr);
                            //jdk 1.8，invoke使用可变参数，直接传入数据会被解释为多个参数
                            method.invoke(t, new Object[]{argumentArr});
                        } else if (argumentType.getName().contains("Boolean") || argumentType.getName().contains("boolean")) {
                            method.invoke(t, Boolean.TRUE);
                        } else {
                            method.invoke(t, paramList.get(0));
                        }
                        break;
                    }
                }
            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new CanoeException(e.getMessage()+" method is: "+currentParserMethodName);
        }
        return t;
    }
}
